<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta http-equiv="Cache-Control" content="no-siteapp">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=1, minimum-scale=1, maximum-scale=1">
<meta name="referrer" content="never">
<meta name="robots" content="index,follow">
<link rel="shortcut icon" href="/favicon.png?v=198964">
<link rel="apple-itouch-icon" href="/favicon.png?v=198964">
<link href="/bundle/index.min.css" rel="stylesheet">
<link href="https://fonts.loli.net/css?family=Merriweather:300,700,700italic,300italic|Open+Sans:700,400" rel="stylesheet">
<link href="https://cdn.staticfile.org/prism/1.16.0/themes/prism.min.css" rel="stylesheet">
<link href="https://cdn.staticfile.org/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
<script>
    function ensureDate(e){return"object"!=typeof e&&(e=new Date(e)),e}function dateFormat(e,t){void 0===t&&(t=e,e=ensureDate());let n={M:(e=ensureDate(e)).getMonth()+1,d:e.getDate(),h:e.getHours(),m:e.getMinutes(),s:e.getSeconds(),q:Math.floor((e.getMonth()+3)/3),S:e.getMilliseconds()},r=new RegExp("([yMdhmsqS])+","g");return t=t.replace(r,function(t,r){let u=n[r];if(void 0!==u)return t.length>1&&(u=(u="0"+u).substring(u.length-2)),u;if("y"===r){return(e.getFullYear()+"").substring(4-t.length)}return t})}
</script>
    <meta name="keywords" content="使用 gulp 搭建CommonJs &amp; ES6 模块化环境,Gulp,ES6,">
    <meta name="description" content="使用 gulp 搭建CommonJs &amp; ES6 模块化环境,Gulp,ES6,">
    <meta name="author" content="江矿先森.">
    <title>使用 gulp 搭建CommonJs &amp; ES6 模块化环境</title>
    <link href="/bundle/iconfont.css" rel="stylesheet">
    <link href="/bundle/reward.css" rel="stylesheet">
    <script src='/bundle/av.min.js'></script>
    <script src='/bundle/valine.min.js'></script>
</head>

<body>
    <article class="container">
        <header class="header-wrap asset">
    <nav class="main-nav">
        <ul class="menu vertical naive">
            <li class="menu-item"><a href="/">Home</a></li>
            <li class="menu-item"><a href="/archive.html">Archive</a></li>
            <li class="menu-item"><a href="/tag.html">Tag</a></li>
            
            <li class="menu-item"><a href="/atom.xml">RSS</a></li>
        </ul>
    </nav>
    <div class="bgs" style="background-image: url(https://pic6.58cdn.com.cn/nowater/webim/big/n_v212fe49b1d6d5492cb1def84eeff4b142.jpg);"></div>
    <div class="vertical">
        <div class="header-wrap-content inner">
            <h3 class="title">Stay before every beautiful thoughts.</h3>
            <h3 class="subtitle">Just be nice, always think twice!</h3>
        </div>
    </div>
</header>
        <article class="main article">
            <h1 class="title">使用 gulp 搭建CommonJs &amp; ES6 模块化环境</h1>
            <section class="info">
                <span class="avatar" style="background-image: url(https://pic1.58cdn.com.cn/nowater/webim/big/n_v2a9773b605e754c8493cf53ca8f5c0d7e.jpg);"></span> <a class="name" href="javascript:;">江矿先森.</a> 
                <span class="date"><script>document.write(dateFormat( 1473065705 *1000, 'yyyy-MM-dd'))</script></span> 
                <span class="tags"><a class="tages" href="/tag/Gulp/index.html">Gulp</a><a class="tages" href="/tag/ES6/index.html">ES6</a></span>
            </section>
            <article class="content"><h1>1. Browserify简介</h1>

<blockquote>
<p>&ldquo;Browserify lets you require(&lsquo;modules&rsquo;) in the browser by bundling up all of your dependencies.&rdquo; - Browserify.org</p>
</blockquote>

<p>上面的描述是摘自 browserify 官网；用通俗的话讲就是：browserify 是一个浏览器端代码模块化工具，可以处理模块之间的依赖关系，让服务器端的 CommonJS 格式的模块可以运行在浏览器端。</p>

<p><img src="" data-src="https://cloud.githubusercontent.com/assets/3995814/11768221/b22531fe-a200-11e5-8e98-8e36d8471bf8.png" alt="" /></p>

<p>browserify的原理：</p>

<ul>
<li>处理代码依赖，将模块打包到一起</li>
</ul>

<p>打包为单个文件存在的问题：</p>

<ul>
<li><p>暂时用不到的代码也会被打包，体积大，首次加载速度慢</p></li>

<li><p>只要一个模块更新，整个文件缓存失效</p></li>
</ul>

<p>注：暂时用不到的代码指不同的页面有不同的 JS 文件，不需要在当前页面引用其他页面的代码即为暂时用不到的代码</p>

<p>Browserify的解决方案：</p>

<ul>
<li>entry point：入口点技术，每个入口点打包一个文件，两个入口点的相同依赖模块单独打包为common.js</li>
</ul>

<h3>安装与配置</h3>

<p>安装 browserify</p>

<p><code>npm install -g browserify</code></p>

<p>引入 browserify</p>

<p><code>import  browserify from 'browserify'</code></p>

<p>基本配置</p>

<pre><code class="language-js">glup.taks('browserify', function() {
  browserify({
     //先处理依赖，入口文件
     entries: ['./foo.js','./main.js'],
     //进行转化
     transform: []
  })
   .bundle() // 多个文件打包成一个文件
   .pipe(source()) // browserify的输出不能直接用做gulp输入，所以需要source进行处理 
   .pipe(gulp.dest('./'));  
})
</code></pre>

<h3>安装一些依赖插件</h3>

<pre><code class="language-bash">npm install --save-dev vinyl-source-stream vinyl-buffer gulp-sourcemaps
</code></pre>

<p><code>vinyl-source-stream</code>: browserify的输出不能直接用着gulp的输入，vinly-source-stream 主要是做一个转化</p>

<p><code>vinyl-buffer</code>: 用于将vinyl流转化为buffered vinyl文件（gulp-sourcemaps及大部分Gulp插件都需要这种格式）</p>

<p><code>gulp-sourcemaps</code>: Source map就是一个信息文件，里面储存着位置信息。也就是说，转换后的代码的每一个位置，所对应的转换前的位置，便于调试</p>

<p><code>Watchify</code>: 加速 browserify 编译</p>

<h1>2. 编写 CommonJS 模块</h1>

<h3>目录结构</h3>

<pre><code>|-- dist/
   |-----bundle.js
|-- src/
   |-----foo.js
   |-----main.js
|--gulpfile.babel.js
|--package.json
</code></pre>

<h3>新建两个模块文件 foo.js, main.js</h3>

<p><code>$ touch foo.js main.js</code></p>

<h3>让我使用 CommonJs 规范来写一些代码</h3>

<blockquote>
<p>CommonJS 规范是为了解决 JavaScript的作用域问题而定义的模块形式，可以使每个模块在它自身的命名空间中执行。
该规范的主要内容是，模块必须通过module.exports 导出对外的变量或接口，通过 require() 来导入其他模块的输出到当前模块作用域中。</p>
</blockquote>

<pre><code class="language-js">// foo.js
// 定义foo.js模块，通过 module.exports 导出对外的变量或接口
let variable = 8;
let sum = (a, b = 6) =&gt; (a + b);
let square = (b) =&gt; {
    return b * b;
};
module.exports.variable = variable;
module.exports.sum = sum;
module.exports.square = square;

// mian.js
// 通过 require() 来导入 foo.js 模块
var bar = require('./foo')
console.log(bar);  // Object
console.log(bar.variable); // 8
console.log(bar.sum(1)); // 7
console.log(bar.square(5)); // 25
</code></pre>

<blockquote>
<p>上面我们使用 ES6 的语法写了两个模块，分别是 foo.js 和 main.js; 在 foo.js 中通过 module.exports 导出对外的变量或接口;在 main.js 中通过 require() 来导入 foo.js 模块，那我们就可以在 mian.js 模块中使用 foo.js 中的变量和接口了。这就是一个最基本的 CommonJs 示例了</p>
</blockquote>

<h3>配置 browserify</h3>

<p>通过第一小节的学习，我们知道要在浏览器中运行 CommonJs 风格的模块代码，就需要借助 browserify 来作为转换工具，下面我们在 gulp.babel.js 中来配置 browserify：</p>

<pre><code class="language-js">// set browserify task
gulp.task('browserify',()=&gt; {
    browserify({
        entries: ['src/js/main.js','src/js/foo.js'],
        debug: true, // 告知Browserify在运行同时生成内联sourcemap用于调试
    })
        .transform(&quot;babelify&quot;, {presets: [&quot;es2015&quot;]})
        .bundle()
        .pipe(source('bundle.js'))
        .pipe(buffer()) // 缓存文件内容
        .pipe(sourcemaps.init({loadMaps: true})) // 从 browserify 文件载入 map
        .pipe(sourcemaps.write('.')) // 写入 .map 文件
        .pipe(gulp.dest('dist/js'))
        .pipe(notify({ message: 'browserify task complete' }));
})
</code></pre>

<p>运行
<code>gulp-browserify</code></p>

<p><img src="" data-src="https://cloud.githubusercontent.com/assets/3995814/11768266/592159dc-a202-11e5-8be3-2d4ddaefe5c3.png" alt="" /></p>

<h3>打开浏览器，查看运行结果(见上面main.js文件的注释)</h3>

<h1>编写 ES6 Module 模块</h1>

<p>上面的代码主要是 CommonJs 模块化的写法，我们再来看看最近火热的 ES6 提供的 Module；让我们使用 ES6 Module 来改写上面的代码：</p>

<pre><code class="language-js">// foo.js
// 定义foo.js模块，通过 exports 导出对外的变量或接口
let variable = 8;
let sum = (a, b = 6) =&gt; (a + b);
let square = (b) =&gt; {
    return b * b;
};
export { variable, sum, square };

// main.js
// 测试一：
// 通过 import 来导入 foo.js 模块
import {variable, sum, square} from './foo';
console.log(variable); // 8
console.log(sum(1)); // 7
console.log(square(5)); // 25

// 测试二：
// 直接引用整个 foo 模块
import bar from './foo';
console.log(bar); // 输出值是undefined，后面做解释

// 测试三：
// 通过 ES6 的语法加载整个 foo模块
import * as bar from './foo'
console.log(bar); // Object
</code></pre>

<h2>总结 CommonJs 和 ES6 Module</h2>

<h3>CommonJs</h3>

<ul>
<li>根据 CommonJS 规范，一个单独的文件就是一个模块。每一个模块都是一个单独的作用域，也就是说，在一个文件定义的变量（包括函数和类），都是私有的，对其他文件是不可见的</li>
<li>通过 module.exports 对象，定义对外接口，其他文件加载该模块，实际上就是读取 module.exports 变量</li>

<li><p>通过 require 命令加载模块文件，然后返回该模块的exports对象</p>

<h3>ES6 Module</h3></li>

<li><p>通过 export 命令用于规定模块的对外接口</p></li>

<li><p>通过 import 命令用于加载其他模块提供的功能</p>

<h3>ES6 Module 与 CommonJs 的区别</h3></li>

<li><p>在ES6中使用 import 取代 require</p></li>

<li><p>在ES6中使用 export 取代 module.exports</p></li>

<li><p>ES6 在编译时就能确定模块的依赖关系，以及输入和输出的变量，而 CommonJs 只能在运行时确定模块的依赖关系以及输入和输出的变量。</p></li>

<li><p>运行时加载: CommonJS 模块就是对象；即在输入时是先加载整个模块，生成一个对象，然后再从这个对象上面读取方法，这种加载称为“运行时加载”</p></li>

<li><p>编译时加载: ES6 模块不是对象，而是通过 export 命令显式指定输出的代码，输入时采用静态命令的形式。即在输入时可以指定加载某个输出值，而不是加载整个模块，这种加载称为“编译时加载”
注：上面提到 ES6 加载模块时是采用指定加载某个输出值的形式，如果你要想加载整个模块，你可以这么做：
<code>import * as customName from './moduleFileName';</code></p></li>
</ul>
</article>
            <section class="author">
                
                <div class="avatar" style="background-image: url(https://pic1.58cdn.com.cn/nowater/webim/big/n_v2a9773b605e754c8493cf53ca8f5c0d7e.jpg);"></div>
                <a class="name" href="javascript:;">江矿先森.</a>
                <div class="intro">前(台)端(菜), 喜欢瞎折腾新技术. 乜野都识少少, 先可以扮代表.</div>
            </section>
            <section class="social">
                <a href="https://github.com/joname1" target="_blank">
                    <i class="iconfont i-github"></i>
                </a>
                <a href="javascript:alert('你电脑中了不知名的病毒, 并抛出了警告 atob(“d3hJZDogam9uYW1lLmxpYW5ndGFu”)')" target="_blank">
                    <i class="iconfont i-wechat"></i>
                </a>
                <a href="https://www.zhihu.com/people/joname-liangtan" target="_blank">
                    <i class="iconfont i-zhihu"></i>
                </a>
                <a href="javascript:alert('对方不想跟你讲话, 并向你扔来一段乱码 atob(“am9uYW1lLmxpYW5ndGFuQGdtYWlsLmNvbQ”)')" target="_blank">
                    <i class="iconfont i-email"></i>
                </a>
            </section>

            <div class="reward">
                <div class="reward-button">赏
                    <span class="reward-code">
                        <span class="alipay-code">
                            <img class="alipay-img wdp-appear" src="https://pic1.58cdn.com.cn/nowater/webim/big/n_v24d5eae197a764eb194b61e366ff263ba.jpg"><b>支付宝扫码</b>
                        </span>
                    <span class="wechat-code">
                            <img class="wechat-img wdp-appear" src="https://pic1.58cdn.com.cn/nowater/webim/big/n_v20ec7ae162f6a4219b8d0b429a2dbc9b3.jpg"><b>微信扫码</b>
                        </span>
                    </span>
                </div>
                
                <p class="reward-notice">如果文章对你有帮助, 扫上方二维码请我喝杯奶茶吧 :p</p>
            </div>

            <div id="comment"></div>
            
        </article>
    </article>
    <footer class="footer clearfix">
    <span class="copyright">
        <script>
            document.write(new Date().getFullYear());
        </script> <i class="fa fa-copyright"></i> Made with <i class="fa fa-heart"></i> using &ltjoname /&gt
        <span id="runtime_span"></span>
    </span>
</footer>
    <script src="/bundle/index.min.js"></script>
    <script src="https://cdn.staticfile.org/prism/1.16.0/prism.min.js"></script>
    <script>
        new Valine({el: '#comment',appId: 'PieJ3iHvVTJ9C5yBudK6sxaT-MdYXbMMI',appKey: 'Yt25unM4vc9wzBvC2lL20Frc',placeholder: 'ヾﾉ≧∀≦)o来啊, 快活啊, 反正有大把时光!!',path: window.location.pathname,avatar: 'retro',pageSize: 10,guest_info: ['nick', 'mail'],lang: 'en'});
    </script>
</body>

</html>